// (c) 2015 Teresa Holfeld
package com.holfeld.teresa.vocabtrainer.provider;

import android.util.SparseArray;

import java.util.ArrayList;
import java.util.List;

/**
 * Collection of VocablePairs.
 *
 * @author Teresa Holfeld, teresa.holfeld@gmail.com
 */
public class Vocabulary {

    /**
     * The name of the vocabulary.
     */
    private String mName;

    /**
     * The first locale.
     */
    private String mLocale1;

    /**
     * The second locale.
     */
    private String mLocale2;

    /**
     * The vocabulary map. Maps vocable pairs (value) to their incremental index (key).
     */
    private SparseArray<VocablePair> mVocabulary;

    /**
     * Creates a new vocabulary with the given name and locales.
     *
     * @param name    the vocabulary name
     * @param locale1 the first locale
     * @param locale2 the second locale
     */
    public Vocabulary(String name, String locale1, String locale2) {
        mName = name;
        mLocale1 = locale1;
        mLocale2 = locale2;
        mVocabulary = new SparseArray<VocablePair>();
    }

    /**
     * Puts vocable if the locales equal the Vocabulary's (can be interchanged).
     * <p/>
     * The method does not check whether the given vocable exists already. It will be added with a
     * new index as key even though it might exist already.
     *
     * @param vocable the vocable pair to put
     * @return true if successfully put, false if locales didn't match
     */
    public boolean put(VocablePair vocable) {

        if ((vocable.getLocale1().equals(mLocale1) && vocable.getLocale2().equals(mLocale2))
                || (vocable.getLocale1().equals(mLocale2)
                && vocable.getLocale2().equals(mLocale1))) {
            int id = mVocabulary.size();
            mVocabulary.put(id, vocable);
            return true;
        }
        return false;
    }

    /**
     * @return list of all vocable pairs in the vocabulary
     */
    public List<VocablePair> getVocablePairs() {
        List<VocablePair> vocablePairs = new ArrayList<VocablePair>();
        for (int i = 0; i < mVocabulary.size(); i++) {
            vocablePairs.add(mVocabulary.valueAt(i));
        }
        return vocablePairs;
    }

    /**
     * @return the size (number of vocable pairs) of the vocabulary
     */
    public int size() {
        return mVocabulary.size();
    }

    /**
     * Gets the vocable pair at given index.
     *
     * @param index the index as key
     * @return the vocable pair at index
     */
    public VocablePair get(int index) {
        return mVocabulary.get(index);
    }

    /**
     * @return the name of the vocabulary
     */
    public String getName() {
        return mName;
    }

    /**
     * @param name the vocabulary name to set
     */
    public void setName(String name) {
        mName = name;
    }

    /**
     * @return the first locale
     */
    public String getLocale1() {
        return mLocale1;
    }

    /**
     * @return the second locale
     */
    public String getLocale2() {
        return mLocale2;
    }

    /**
     * Deletes the given vocable pair from the vocabulary.
     * <p/>
     * The method does not check if there exist several vocable pairs equal to the given one, but
     * will rather take the first one it encounters in the linear search.
     * <p/>
     * After deleting the vocable pair, it will re-organize the map so that the indexes are
     * incremental. This means, the index (key) values of the vocables will change.
     *
     * @param vocablePair vocable pair to delete
     * @return true if the vocable pair was successfully deleted, false if it didn't exist
     */
    public boolean delete(VocablePair vocablePair) {
        boolean deletedSomething = false;
        for (int i = 0; i < mVocabulary.size(); i++) {
            if (mVocabulary.valueAt(i).equals(vocablePair)) {
                mVocabulary.removeAt(i);
                deletedSomething = true;
                break;
            }
        }
        List<VocablePair> vocablePairs = getVocablePairs();
        mVocabulary.clear();
        for (VocablePair vocable : vocablePairs) {
            mVocabulary.put(mVocabulary.size(), vocable);
        }
        return deletedSomething;
    }

    /**
     * Looks up the VocablePair with the given vocables, and deletes the vocable pair from the
     * vocabulary. It does not matter in which order the vocables are given or to which locales
     * they belong.
     * <p/>
     * The method does not check if there exist several vocable pairs equal to the given one, but
     * will rather take the first one it encounters in the linear search.
     * <p/>
     * After deleting the vocable pair, it will re-organize the map so that the indexes are
     * incremental. This means, the index (key) values of the vocables will change.
     *
     * @param vocable1 the first vocable of the VocablePair to delete
     * @param vocable2 the second vocable of the VocablePair to delete
     * @return true if the vocable pair was successfully deleted, false if it didn't exist
     */
    public boolean delete(String vocable1, String vocable2) {
        for (int i = 0; i < mVocabulary.size(); i++) {
            VocablePair vocablePair = mVocabulary.valueAt(i);
            String vocablePairVocable1 = vocablePair.getVocable1();
            String vocablePairVocable2 = vocablePair.getVocable2();
            if (equal(vocable1, vocable2, vocablePairVocable1, vocablePairVocable2)) {
                return delete(vocablePair);
            }
        }
        return false;
    }

    private static boolean equal(String vocable1a, String vocable2a,
                                 String vocable1b, String vocable2b) {
        if (vocable1a == null || vocable2a == null || vocable1b == null || vocable2b == null) {
            return false;
        }
        if (vocable1a.equals(vocable1b) && vocable2a.equals(vocable2b) ||
                vocable1a.equals(vocable2b) && vocable2a.equals(vocable1b)) {
            return true;
        }
        return false;
    }

    /**
     * Checks whether the given vocable pair exists in the vocabulary.
     *
     * @param vocable the vocable pair to check for existence
     * @return true if it exists, false otherwise
     */
    public boolean exists(VocablePair vocable) {
        for (int i = 0; i < mVocabulary.size(); i++) {
            if (mVocabulary.valueAt(i).equals(vocable)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Checks whether the vocabulary contains a VocablePair with the two given vocables. It does
     * not matter in which order the vocables are given or to which locales they belong.
     *
     * @param vocable1 the first vocable
     * @param vocable2 the second vocable
     * @return true if the vocabulary contains a VocablePair with these vocables, false otherwise
     */
    public boolean contains(String vocable1, String vocable2) {
        for (int i = 0; i < mVocabulary.size(); i++) {
            VocablePair vocablePair = mVocabulary.valueAt(i);
            String vocablePairVocable1 = vocablePair.getVocable1();
            String vocablePairVocable2 = vocablePair.getVocable2();
            if (vocable1.equals(vocablePairVocable1) && vocable2.equals(vocablePairVocable2) ||
                    vocable1.equals(vocablePairVocable2) && vocable2.equals(vocablePairVocable1)) {
                return true;
            }
        }
        return false;
    }
}
